﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Package;
using Microsoft.VisualStudio.TextManager.Interop;
using NBusiness.ESharp;
using NBusiness.CodeDom.Compiler;
using NBusiness.CodeDom;
using System.Reflection;
using EnvDTE;
using NBusiness.Utilities;
using NBusiness.ESharp.Compiler;
using NBusiness.CodeDom.Services;

namespace NBusiness.VisualStudio
{
    class ESharpScope : AuthoringScope
    {
        ParseRequest _request;
        ProjectParseInfo _parseInfo;

        public ESharpScope(ProjectParseInfo parseInfo, ParseRequest request)
        {
            _request = request;
            _parseInfo = parseInfo;
        }

        public override string GetDataTipText(int line, int col, out TextSpan span)
        {
            span = new TextSpan();
            return null;
        }

        public override Declarations GetDeclarations(IVsTextView view, int line, int col, TokenInfo info, ParseReason reason)
        {
            List<ESharpDeclaration> declarations = new List<ESharpDeclaration>();

            if(reason == ParseReason.DisplayMemberList)
            {
                EntityRoot root = null;
                EntityElement[] elements = null;
                lock (_parseInfo)
                {
                    root = _parseInfo.Root;
                    elements = _parseInfo.Elements;
                }

                if (root != null)
                {
                    EntityElement element = (root as ICompileUnit).GetElementByLocation(_request.FileName, line, col);
                    if (element != null)
                    {
                        ICompileUnit unit = (root as ICompileUnit).GetUnitByElement(element);
                        info.StartIndex = element.Column;
                        info.EndIndex = element.Column + element.Value.Length - 1;

                        EntityElement previous = null;
                        foreach (EntityElement e in elements)
                        {
                            if (e == element)
                                break;

                            if(e.Type.IsSignificant)
                                previous = e;
                        }

                        if (previous != null)
                        {
                            if (previous.Type == ESharpEntityElementTypes.FieldDeclaration)
                            {
                                GetFieldTypeDeclarations(declarations, element);
                            }
                            else if (previous.Type == ESharpEntityElementTypes.AccessDeclaration)
                            {
                                GetAllowDenyDeclarations(declarations, element);
                            }
                            else if (previous.Type == ESharpEntityElementTypes.EntityAs)
                            {
                                GetAvailableTemplates(declarations, element, unit);
                            }
                        }
                    }
                }
            }
            return new ESharpDeclarations(declarations.ToArray());
        }

        #region Declaration builders

        #region Allow deny declarations
        private void GetAllowDenyDeclarations(List<ESharpDeclaration> declarations, EntityElement element)
        {
            declarations.Add(new ESharpDeclaration { Title = "allow", Type = element.Type });
            declarations.Add(new ESharpDeclaration { Title = "deny", Type = element.Type });
        }
        #endregion

        #region Field declarations
        private void GetFieldDeclarations(List<ESharpDeclaration> declarations, EntityElement element, EntityCompileUnit unit)
        {
            foreach (EntityField field in ((unit as EntityField).Parent as Entity).Fields)
            {
                if (field != unit)
                    declarations.Add(new ESharpDeclaration { Title = field.Name, Type = element.Type });
            }
        }
        #endregion

        #region Access type declarations
        private void GetAccessTypeDeclarations(List<ESharpDeclaration> declarations, EntityElement element)
        {
            declarations.Add(new ESharpDeclaration { Title = "get", Type = element.Type });
            declarations.Add(new ESharpDeclaration { Title = "set", Type = element.Type });
        }
        #endregion

        #region Authorize type declarations
        private void GetAuthorizeTypeDeclarations(List<ESharpDeclaration> declarations, EntityElement element)
        {
            declarations.Add(new ESharpDeclaration { Title = "delete", Type = element.Type });
            declarations.Add(new ESharpDeclaration { Title = "fetch", Type = element.Type });
            declarations.Add(new ESharpDeclaration { Title = "insert", Type = element.Type });
            declarations.Add(new ESharpDeclaration { Title = "update", Type = element.Type });
        }
        #endregion

        #region Field type declarations

        private void GetFieldTypeDeclarations(List<ESharpDeclaration> declarations, EntityElement element)
        {
            foreach (string name in ESharpField.GetFieldTypeNames())
            {
                declarations.Add(new ESharpDeclaration { Title = name.ToLower(), Type = element.Type });
            }
        }

        #endregion

        #region Validate property declarations

        private void GetValidatePropertyDeclarations(List<ESharpDeclaration> declarations, EntityElement element, EntityCompileUnit unit)
        {
            if (unit != null && unit is IHasParent)
            {
                foreach (EntityField f in ((unit as IHasParent).Parent as Entity).Fields)
                    declarations.Add(new ESharpDeclaration { Title = f.Name, Type = element.Type });

                foreach (EntityRelationship r in ((unit as IHasParent).Parent as Entity).Relationships)
                {
                    if (r.From == ((unit as IHasParent).Parent as Entity))
                        declarations.Add(new ESharpDeclaration { Title = r.Name, Type = element.Type });
                }
            }
        }

        #endregion

        #region Template declarations

        private void GetAvailableTemplates(List<ESharpDeclaration> declarations, EntityElement element, ICompileUnit unit)
        {
            IFrameworkService frameworkService = null;
            lock (_parseInfo)
                frameworkService = _parseInfo.Provider.GetService(typeof(IFrameworkService)) as IFrameworkService;

            EntityTemplate[] templates = frameworkService.GetTemplates();
            
            Entity entity = unit as Entity;
            if (entity == null)
            {
                IHasParent hasParent = unit as IHasParent;
                if (hasParent != null)
                    entity = hasParent.Parent as Entity;
            }

            if (entity != null)
            {
                foreach (EntityTemplate template in templates)
                {
                    string fullname = template.Name;
                    string name = fullname.Substring(fullname.LastIndexOf('.') + 1);
                    string partialName = (name.ToLower().EndsWith("template") ?
                        name.Remove(name.ToLower().IndexOf("template")) :
                        name);
                    string @namespace = fullname.Remove(fullname.LastIndexOf('.'));

                    var found = from t in entity.As
                                where
                                    t.Name == fullname ||
                                    t.Name == name ||
                                    t.Name == partialName
                                select t;

                    if (found.Count() == 0)
                    {
                        string[] namespaces = (from @using in entity.Using select @using.Namespace).ToArray();

                        if (namespaces.Contains(@namespace))
                            declarations.Add(new ESharpDeclaration { Title = partialName, Type = element.Type });
                        else
                            declarations.Add(new ESharpDeclaration { Title = @namespace + "." + partialName, Type = element.Type });
                    }
                }
            }
        }

        #endregion 

        #endregion
        
        public override Methods GetMethods(int line, int col, string name)
        {
            return null;
        }

        public override string Goto(Microsoft.VisualStudio.VSConstants.VSStd97CmdID cmd, Microsoft.VisualStudio.TextManager.Interop.IVsTextView textView, int line, int col, out Microsoft.VisualStudio.TextManager.Interop.TextSpan span)
        {
            span = new TextSpan();
            return null;
        }
    }
}
